﻿//************************************************************************************************
// Copyright © 2003-2012 Steven M. Cohn. All Rights Reserved.
//
// Facility:	Microsoft Development Environment 2012  Version 11.0.50727.1
// Environment:	Microsoft .NET Framework 4.5            Version v4.0.30319
//
// Description: Processes cookies to determine their state
//
// Revision History:
// -Who--------------- -When---------- -What------------------------------------------------------
// Steven M. Cohn		18-Oct-2003		New
// Steven M. Cohn		12-Oct-2004		VS.NET 2005, .NET 2.0
// Steven M. Cohn		20-Nov-2012		Refresh! VS2010 .NET 4.5 64-bit
//************************************************************************************************

namespace CookieChipper
{
	using System;
	using System.Collections.Specialized;
	using System.ComponentModel;
	using System.Globalization;
	using System.IO;


	/// <summary>
	/// Processes cookies to determine their state.
	/// </summary>

	internal class Oven : INotifyPropertyChanged
	{
		private readonly string JarPath = @"River Software\CookieChipper";
		private readonly string JarFile = "CookieJar.dat";

		private string path;						// full path of jar
		private DateTime lastRun;					// last time we ran
		private StringCollection locked;			// list of known locked cookies
		private StringCollection favorites;			// list of favorites


		//========================================================================================
		// Lifecycle
		//========================================================================================

		/// <summary>
		/// 
		/// </summary>

		public Oven ()
			: this(true)
		{
		}

		public Oven (bool withFavorites)
		{
			locked = new StringCollection();
			favorites = new StringCollection();

			if (CookieIsAjar())
			{
				PilferCookies();
			}

			if (withFavorites)
			{
				LoadFavorites();
			}
		}


		private bool CookieIsAjar ()
		{
			path = Path.Combine(
				Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
				JarPath);

			if (!Directory.Exists(path))
			{
				Directory.CreateDirectory(path);
				return false;
			}

			path = Path.Combine(path, JarFile);
			if (!File.Exists(path))
			{
				return false;
			}

			return true;
		}


		private void PilferCookies ()
		{
			try
			{
				using (var reader = File.OpenText(path))
				{
					// first line contains last-run datetime
					string line = reader.ReadLine();
					if (line != null)
					{
						long filetime;
						if (long.TryParse(line,
							NumberStyles.HexNumber, CultureInfo.InvariantCulture, out filetime))
						{
							lastRun = DateTime.FromFileTime(filetime);
						}
						else
						{
							lastRun = DateTime.Now;
						}

						// subsequent lines contain locked cookie names/domains
						while ((line = reader.ReadLine()) != null)
						{
							locked.Add(line);
						}
					}
				}
			}
			catch (Exception)
			{
			}
		}


		private void LoadFavorites ()
		{
			string path = Environment.GetFolderPath(Environment.SpecialFolder.Favorites);
			if (Directory.Exists(path))
			{
				using (var files = Directory.EnumerateFiles(
					path, "*.url", SearchOption.AllDirectories).GetEnumerator())
				{
					while (files.MoveNext())
					{
						string url = IniProvider.Read(files.Current, "InternetShortcut", "URL");

						if (!String.IsNullOrEmpty(url))
						{
							Uri uri = new Uri(url);

							string host;
							if (uri.Host.StartsWith("www.") && (uri.Host.Length > 4))
							{
								host = uri.Host.Substring(4);
							}
							else
							{
								host = uri.Host;
							}

							if (!favorites.Contains(host))
							{
								favorites.Add(host);
							}
						}
					}
				}
			}
		}



		//========================================================================================
		// Properties
		//========================================================================================

		/// <summary>
		/// Signal when properties have changed.
		/// </summary>

		public event PropertyChangedEventHandler PropertyChanged;


		/// <summary>
		/// 
		/// </summary>

		public int Locked
		{
			get { return locked.Count; }
		}


		//========================================================================================
		// Methods
		//========================================================================================

		/// <summary>
		/// 
		/// </summary>
		/// <param name="cookie"></param>

		public void Add (Cookie cookie)
		{
			if (!locked.Contains(cookie.Domain))
			{
				locked.Add(cookie.Domain);
			}

			Bake(cookie);

			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs("Locked"));
			}
		}


		/// <summary>
		/// 
		/// </summary>
		/// <param name="cookie"></param>

		public void Bake (Cookie cookie)
		{
			if (locked.Contains(cookie.Domain))
			{
				// favorite cookie to be preserved
				cookie.State |= CookieState.Locked;
			}

			if (IsFavorite(cookie))
			{
				cookie.State |= CookieState.Favorite;
			}

			DateTime now = DateTime.Now;
			if (!cookie.Expires.Equals(DateTime.MinValue) &&
				(now.Subtract(cookie.Expires).Days > 0))
			{
				// expired
				cookie.State |= CookieState.Stale;
			}

			if (now.Subtract(cookie.Updated).Days > 365)
			{
				// not updated in over a year
				cookie.State |= CookieState.Moldy;
			}

			if (lastRun.Subtract(cookie.Created).Days < 0)
			{
				// new since last inspection
				cookie.State |= CookieState.Raw;
			}

			if (!File.Exists(cookie.Path))
			{
				cookie.State |= CookieState.Rogue;
			}

			// always add this if other flags subsequently get resolved
			cookie.State |= CookieState.Crispy;
		}


		private bool IsFavorite (Cookie cookie)
		{
			if (!String.IsNullOrEmpty(cookie.Domain))
			{
				return favorites.Contains(HostOf(cookie.Domain));
			}

			return false;
		}


		private string HostOf (string domain)
		{
			Uri uri = new Uri("http://" + domain);

			string host;
			if (uri.Host.StartsWith("www.") && (uri.Host.Length > 4))
			{
				host = uri.Host.Substring(4);
			}
			else
			{
				host = uri.Host;
			}

			return host;
		}


		/// <summary>
		/// 
		/// </summary>
		/// <param name="cookie"></param>
		/// <returns></returns>

		public bool IsLocked (Cookie cookie)
		{
			return locked.Contains(cookie.Domain);
		}


		/// <summary>
		/// 
		/// </summary>
		/// <param name="cookie"></param>

		public void Remove (Cookie cookie)
		{
			if (locked.Contains(cookie.Domain))
			{
				locked.Remove(cookie.Domain);
			}

			Bake(cookie);

			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs("Locked"));
			}
		}


		/// <summary>
		/// 
		/// </summary>

		public void Save ()
		{
			using (var writer = File.CreateText(path))
			{
				writer.WriteLine(DateTime.Now.ToFileTime().ToString("X16"));
				foreach (string name in locked)
				{
					writer.WriteLine(name);
				}
			}
		}
	}
}
